unit uFleet;

{
  Building a ship will place a new ship in the ship array, but it will not move until its either complete or forced moved by the player (halfbaked). It also visible above the planet at that location.
}

interface

uses cuVector, SysUtils, cuSimpleArrays, Math, uDefs;

type
  tShip = class( TObject ) // A ship can only be controlled as part of a fleet, either as 1 ship or many.
  public
    shipOwnerID         : integer;
    shipName            : string;
    shipHealth          : integer;
    shipExp             : int64;   // Each ship gains XP during its life. When its killed, the XP is distributed between the killing fleets ships.
    shipAgility         : integer; // Damage shot Avoidance. Calced using Thrust v Mass
    shipMass            : integer; // More mass reduces thrust effectiveness.
    shipThrust          : integer; // Amount of thrust to drive the ship forward.
    shipArmor           : integer; // More armor means it can survive longer
    shipDamage          : integer; // Damage per hit shot
    shipInitiative      : integer; // Order of fire
    shipStatus          : integer; // Operational status of ship (refit/building).
    shipBlueprintID     : integer;

    shipComponant       : TIntegerArray; //points to the componant database, yet to be completed.

    constructor Create();
    destructor Destroy(); override;
    procedure resetShip();
  end;

  tFleet = class( TObject )
    fleetShip                  : TIntegerArray;//array of ShipID

    fleetEmpireID              : integer;
    fleetName                  : string;
    fleetHealth                : integer; // average health of all ships in fleet.
    fleetSpeed                 : integer; //Speed at which the fleet can move
    fleetLaunchDate            : integer; //States when the fleet launched
    fleetWeeksToDestination    : integer; //Estrimated time for arrival (based on turns, 1 week = 1 turn).
    fleetPathMode              : enumPathMode; //Closest target or random target
    fleetStatus                : enumFleetStatus; //Travelling/stationary.
    fleetXP                    : integer;
    fleetActive                : boolean;

    fleetCurrentVector,
    fleetStartVector,
    fleetEndVector             : TVector2D;
    fleetStartID               : integer; //Start ObjectID to move from
    fleetEndID                 : integer; //End ObjectID to move to

    constructor Create();
    destructor Destroy(); override;
    procedure resetFleet();

    procedure linkEmpire(newEmpireID:integer);
    procedure linkShip(newShipID:integer);

    procedure setVector(newVector:TVector2D;newAstroID:integer);
    procedure setEndPoint();
    procedure setSpeed(newSpeed:integer);
    procedure setPathMode(newPathMode:enumPathMode);
    procedure changePathMode();

    procedure launchFleet(newVector:TVector2D;newAstroID,currentAge:integer);
    procedure moveFleet(currentAge:integer);

    function fleetInOrbit(theAstroID:integer = -1):boolean;
    function fleetHasArrived():boolean;
    function fleetIsMoving():boolean;

    function fleetETA():integer;
    function fleetTimeLeft(currentAge:integer):integer;
  end;

implementation

uses cuDebug;

//============= TShip
constructor tShip.Create();
begin
  inherited;

  shipComponant := TIntegerArray.Create('[TShip]');

  resetShip();
end;

destructor tShip.Destroy();
begin
  resetShip();

  FreeAndNil( shipComponant );

  inherited;
end;

procedure tShip.resetShip();
begin
  shipOwnerID         := -1;
  shipName            := '';
  shipHealth          := 100;
  shipExp             := 0;
  shipAgility         := 0;
  shipMass            := 0;
  shipThrust          := 0;
  shipArmor           := 0;
  shipDamage          := 0;
  shipInitiative      := 0;
  shipStatus          := 0;
  shipBlueprintID     := -1;

  shipComponant.ClearArray('resetShip');
end;

//============= TFleet
constructor tFleet.Create();
begin
  inherited;

  fleetCurrentVector  := TVector2D.Create();
  fleetStartVector    := TVector2D.Create();
  fleetEndVector      := TVector2D.Create();

  fleetShip           := TIntegerArray.Create('[TFleet]');

  resetFleet();
end;

destructor tFleet.Destroy();
begin
  resetFleet();

  FreeAndNil( fleetCurrentVector   );
  FreeAndNil( fleetStartVector );
  FreeAndNil( fleetEndVector   );

  inherited;
end;

procedure tFleet.resetFleet();
begin
  fleetShip.ClearArray('resetFleet');

  fleetEmpireID              := -1;
  fleetName                  := '';
  fleetHealth                := 100;
  setSpeed( 5 );
  fleetLaunchDate            := -1;
  fleetWeeksToDestination    := 0;
  fleetPathMode              := pathNONE;
  fleetStatus                := fleetNONE;
  fleetXP                    := 0;
  fleetActive                := TRUE;

  fleetCurrentVector.setVector2D(0,0);
  fleetStartVector.setVector2D(0,0);
  fleetEndVector.setVector2D(0,0);
  fleetStartID               := -1;
  fleetEndID                 := -1;
end;

procedure tFleet.linkEmpire(newEmpireID:integer);
begin
  DevConsole.Print(LOG_DEBUG2, '[TFleet   ] linkEmpire( '+IntToStr(newEmpireID)+' )');

  fleetEmpireID := newEmpireID;
end;
procedure tFleet.linkShip(newShipID:integer);
begin
end;

procedure tFleet.setVector(newVector:TVector2D;newAstroID:integer);
begin
  fleetCurrentVector.setVector2D( newVector.X, newVector.Y );
  fleetStartVector.setVector2D( newVector.X, newVector.Y );
  fleetEndVector.setVector2D( newVector.X, newVector.Y );
  fleetStartID  := newAstroID;
  fleetEndID    := newAstroID;

  fleetStatus   := fleetORBIT;

  DevConsole.Print(LOG_DEBUG2, '[TFleet   ] setVector( Current: '+fleetCurrentVector.ToX_Y(':')+', '+fleetStartVector.ToX_Y(':')+' <--------> '+fleetEndVector.ToX_Y(':'));
end;
procedure TFleet.setEndPoint();
begin
  fleetStartVector.setVector2D( fleetEndVector.X, fleetEndVector.Y );
  fleetCurrentVector.setVector2D( fleetEndVector.X, fleetEndVector.Y );
  fleetStartID       := fleetEndID;
end;
procedure TFleet.setSpeed(newSpeed:integer);
begin
  fleetSpeed := newSpeed;
end;
procedure TFleet.setPathMode(newPathMode:enumPathMode);
begin
  fleetPathMode := newPathMode;
end;
procedure TFleet.changePathMode();
begin
  if (Random(100) < 5)or(fleetPathMode = pathNONE) then
  case Random(2) of
    0: setPathMode( pathHOME );
    1: setPathMode( pathFLEET );
//    2: setPathMode( pathRANDOM );
  end;
end;

procedure tFleet.launchFleet(newVector:TVector2D;newAstroID,currentAge:integer);
begin
  fleetEndVector.setVector2D( newVector.X, newVector.Y );
  fleetEndID  := newAstroID;
  fleetStatus := fleetMOVING;

  // Set the arrival date by adding travel time (distance / distance per day) to current date
  fleetLaunchDate         := currentAge;
//  fleetWeeksToDestination := Ceil( fleetStartVector.getDistance2D( fleetEndVector.X, fleetEndVector.Y ) / fleetSpeed); // Basically sets out how many steps to the target vector
  fleetWeeksToDestination := Ceil( fleetStartVector.getTravel2D( fleetEndVector.X, fleetEndVector.Y, fleetSpeed ) ); // Basically sets out how many steps to the target vector

  DevConsole.Print(LOG_DEBUG1, '[TFleet   ] launchFleet - Fleet #'+IntToStr(newAstroID)+', stationed at #'+fleetCurrentVector.ToX_Y(':')+', launching from #'+fleetStartVector.ToX_Y(':')+' <--------> destination #'+fleetEndVector.ToX_Y(':')+
                             ', ETA #'+IntToStr(fleetWeeksToDestination) );
end;

procedure tFleet.moveFleet(currentAge:integer); // Moves the fleet alongs its path
var
  WeeksCompleted : integer;
  PercentComplete : real;
  RelativeVector : TVector2D;
begin
  if ( fleetIsMOVING() ) then
  begin
    WeeksCompleted  := fleetWeeksToDestination - (fleetLaunchDate + fleetWeeksToDestination - currentAge);
    PercentComplete := WeeksCompleted / fleetWeeksToDestination;

    RelativeVector := TVector2D.Create();

    RelativeVector.X := fleetStartVector.X - fleetEndVector.X;
    RelativeVector.Y := fleetStartVector.Y - fleetEndVector.Y;

    RelativeVector.X := round(RelativeVector.X * PercentComplete);
    RelativeVector.Y := round(RelativeVector.Y * PercentComplete);

    fleetCurrentVector.X := fleetStartVector.X - RelativeVector.X;
    fleetCurrentVector.Y := fleetStartVector.Y - RelativeVector.Y;

    if ( fleetETA() <= currentAge ) then // Basically sets it self to orbit the target
    begin
      fleetStatus        := fleetARRIVED;
    end;
  end;
end;

function TFleet.fleetInOrbit(theAstroID:integer = -1):boolean;
begin
  result := FALSE;

  if (fleetStatus = fleetORBIT)and(theAstroID = -1) then result := TRUE;

  if ((fleetStatus = fleetORBIT)and(theAstroID > -1)) then
  begin
    if (fleetEndID = theAstroID) then result := TRUE
    else                              result := FALSE;
  end;
end;
function TFleet.fleetHasArrived():boolean;
begin
  case fleetStatus of
    fleetARRIVED : result := TRUE;
    else result := FALSE;
  end;
end;
function TFleet.fleetIsMoving():boolean;
begin
  case fleetStatus of
    fleetMOVING : result := TRUE;
    else result := FALSE;
  end;
end;

function tFleet.fleetETA():integer;
begin
  result := fleetLaunchDate + fleetWeeksToDestination;
end;

function tFleet.fleetTimeLeft(currentAge:integer):integer;
begin
  result := (fleetLaunchDate + fleetWeeksToDestination - currentAge);

  if (result < 0) then result := 0;
  
end;

end.
